#pragma once

#include "util.hpp"
#include "room.hpp"
#include <mutex>
#include <list>
#include <condition_variable>
#include <thread>

template<class T>
class match_queue
{
public:
    match_queue()
    {}
    int size() {std::unique_lock<std::mutex> lock(_mutex); return _list.size();}
    bool empty() {std::unique_lock<std::mutex> lock(_mutex);return _list.empty();}
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
    void push(const T& data) 
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);

        _cond.notify_all();
    }
    // 出队数据
    bool pop(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_list.empty())//可能会在其他的线程被移除掉了
        {
            return false;
        }
        data = _list.front();
        _list.pop_front();
        return true;
    }
    //移除
    void remove(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }
    ~match_queue()
    {}
private:
    std::list<T> _list;//双向链表，因为需要删除数据
    std::mutex _mutex;//线程安全
    std::condition_variable _cond;//主要为了阻塞消费者，队列中<2的阻塞
};


class matcher
{
private:
    void handle_match(match_queue<uint64_t> &mq)
    {
        while(1)
        {
            // 1.判断是否大于二
            while(mq.size() < 2)
            {
                mq.wait();
            }
            // 2.走下来代表人数够了，出队两个玩家
            uint64_t uid1,uid2;
            bool ret = mq.pop(uid1);
            if(!ret) {continue;}
            ret = mq.pop(uid2);//第一个成功了，第二个可能失败
            if(!ret) {this->add(uid1); continue;}
            // 3.校验两个玩家是否掉线，如果有人掉线，要把另一个人重新加入队列
            wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
            if(!conn1.get())
            {
                this->add(uid2);
                continue;
            }
            wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
            if(!conn2.get())
            {
                this->add(uid1);
                continue;
            }
            // 4.为两个人创建房间，并将玩家加入房间中,失败重新加入匹配队列
            room_ptr rp = _rm->create_room(uid1,uid2);
            if(!rp)
            {
                this->add(uid1);
                this->add(uid2);
                continue;
            }
            // 5.对两个玩家进行响应
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string body;
            json_util::Serialize(resp,body);
            conn1->send(body);
            conn2->send(body);
        }
    }
    void th_normal_entry()
    {
        return handle_match(_q_normal);
    }
    void th_high_entry()
    {
        return handle_match(_q_high);
    }
    void th_super_entry()
    {
        return handle_match(_q_super);
    }
public:
    matcher(room_manager* rm,user_table* ut,online_manager* om)
        :_rm(rm),
        _ut(ut),
        _om(om),
        _th_normal(std::thread(&matcher::th_normal_entry,this)),
        _th_high(std::thread(&matcher::th_high_entry,this)),
        _th_super(std::thread(&matcher::th_super_entry,this))
    {
        DBG_LOG("游戏匹配模块初始化完毕");
    }
    bool add(uint64_t uid)
    {
        //根据天梯分数，添加到不同的匹配队列
        Json::Value user;
        bool ret = _ut->select_by_id(uid,user);
        if(!ret)
        {
            DBG_LOG("获取玩家：%ld 信息失败",uid);
            return false;
        }
        int score = user["score"].asInt();

        // 2.添加到指定队列中
        if(score < 2000)
        {
            _q_normal.push(uid);
        }
        else if(score >= 2000 && score < 3000)
        {
            _q_high.push(uid);
        }
        else
        {
            _q_super.push(uid);
        }
        return true;
    }
    bool del(uint64_t uid)
    {
        //根据天梯分数，在不同的匹配队列删除
        Json::Value user;
        bool ret = _ut->select_by_id(uid,user);
        if(!ret)
        {
            DBG_LOG("获取玩家：%ld 信息失败",uid);
            return false;
        }
        int score = user["score"].asInt();

        // 2.指定队列中删除
        if(score < 2000)
        {
            _q_normal.remove(uid);
        }
        else if(score >= 2000 && score < 3000)
        {
            _q_high.remove(uid);
        }
        else
        {
            _q_super.remove(uid);
        }
        return true;
    }
    ~matcher()
    {}
private:
    match_queue<uint64_t> _q_normal;//青铜
    match_queue<uint64_t> _q_high;//白银
    match_queue<uint64_t> _q_super;//黄金
    std::thread _th_normal;
    std::thread _th_high;
    std::thread _th_super;

    room_manager* _rm;//肯定需要创建房间
    user_table* _ut;//需要知道用户的天梯分数
    online_manager* _om;//对用户的管理
};